﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace _80X_CG_Frame.Workers
{
    /// <summary>
    /// 稳定高效的日志(并不)
    /// </summary>
    public static class Log
    {
        /// <summary>
        /// 日志缓存区
        /// </summary>
        public static List<string> logs;
        private static object log_lock;
        /// <summary>
        /// 日志写入线程
        /// </summary>
        public static LogWorker logWorker;
        static Log()
        {
            logs = new List<string>();
            log_lock = new object();
            logWorker = new LogWorker();
            logWorker.Start();
        }
        /// <summary>
        /// 写日志到日志队列
        /// </summary>
        /// <param name="s">日志文本</param>
        public static void Write(string s)
        {
            lock (log_lock)
            {
                if (s.Length != 0)
                    logs.Add(string.Concat(DateTime.Now.ToString("[HH:mm:ss]"), s, "\r\n"));
            }
        }
        /// <summary>
        /// 取出日志队列操作
        /// </summary>
        /// <returns>队头的日志</returns>
        public static string[] Read()
        {
            lock (log_lock)
            {
                string[] t_list = logs.ToArray();
                logs.Clear();
                return t_list;
                //return logs.Dequeue();
            }
        }

        /// <summary>
        /// 停止日志队列
        /// </summary>
        public static void Stop()
        {
            logWorker.Cancel();
        }

        public static string LogPath()
        {
            return logWorker.log_path;
        }

        /// <summary>
        /// 压缩日志文件(不填则默认压缩*.log)
        /// </summary>
        /// <param name="how_long">多久之前</param>
        /// <param name="days">周期</param>
        /// <param name="folder_path">文件夹路径</param>
        /// <param name="patterns">文件后缀列表</param>
        public static void ZipLog(TimeSpan how_long, int days, string folder_path, params string[] patterns)
        {
            Task.Run(new Action(() =>
            {
                if(patterns.Length == 0)
                    patterns = new string[]{"*.log"};
                //创建临时文件夹
                string temp_folder = folder_path + @"\temp";

                Directory.CreateDirectory(temp_folder);

                foreach (string pattern in patterns)
                {
                    string 后缀名 = pattern.Split('.').Last();
                    //过期日志文件列表
                    List<FileInfo> outdated_files = new List<FileInfo>();
                    //过期时间
                    DateTime outdated_time = DateTime.Now - how_long;
                    //筛选过期日志文件并排序
                    outdated_files = Directory.EnumerateFiles(folder_path, pattern, SearchOption.AllDirectories)
                        .Select(f => new FileInfo(f))
                        .Where(f => f.CreationTime < outdated_time)
                        .OrderBy(f => f.CreationTime)
                        .ToList();
                    //日志文件按周期分组
                    int groups = 0;
                    if (outdated_files.Count != 0)
                    {
                        groups = (int)Math.Floor((double)(outdated_files.Last().CreationTime - outdated_files.First().CreationTime).Days / days);
                    }
                    for (int i = 0; i < groups; i++)
                    {
                        DateTime period_start = outdated_files.First().CreationTime.Date + TimeSpan.FromDays(i * days);
                        DateTime period_end = period_start.AddDays(days);
                        List<FileInfo> t_list = outdated_files.Where(f => period_start <= f.CreationTime && f.CreationTime < period_end).ToList();
                        if (t_list.Count == 0)
                            continue;
                        //日志压缩文件名:20180101-20180102.zip
                        string zip_name = folder_path + @"\" + period_start.ToString("yyyyMMdd") + "-" + period_end.ToString("yyyyMMdd") + "." + 后缀名 + ".zip";
                        t_list.ForEach(f => f.MoveTo(temp_folder + @"\" + f.Name));
                        try
                        {
                            if (File.Exists(zip_name))
                                File.Delete(zip_name);
                            System.IO.Compression.ZipFile.CreateFromDirectory(temp_folder, zip_name);
                        }
                        catch (IOException e)
                        {
                            Write(e.ToString());
                        }
                        Directory.EnumerateFiles(temp_folder, pattern, SearchOption.TopDirectoryOnly).ToList().ForEach(f => File.Delete(f));
                        System.Threading.Thread.Sleep(100);

                    }
                }
                if(Directory.Exists(temp_folder))
                    Directory.Delete(temp_folder);
            }));

        }

    }
    /// <summary>
    /// 高效日志工作线程(并不)
    /// </summary>
    public class LogWorker : WorkerBase, INotifyPropertyChanged
    {
        private StreamWriter log_file;
        public string log_path;
        private string log_stack;
        //private Stopwatch sw;
        //private long write_count;
        //private List<Task> tasks;

        /// <summary>
        /// 日志栈
        /// </summary>
        public string Log_stack
        {
            get => log_stack; set
            {
                /*if (value != this.log_stack)
                {
                    this.log_stack = value;
                    NotifyPropertyChanged();
                }*/
                this.log_stack = value;
                NotifyPropertyChanged();
            }
        }
        


        public LogWorker()
        {
            log_stack = "";

        }

        protected override void Run()
        {
            Init();
            while (loop)
            {
                Work();
                System.Threading.Thread.Sleep(100);
            }
            Exit();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            //压缩日志
            Log.ZipLog(
                TimeSpan.FromDays(Convert.ToDouble(GLOBAL.config.Get("log_zip_period"))),
                Convert.ToInt32(GLOBAL.config.Get("log_zip_period")),
                GLOBAL.config.Get("log_folder_path"), "*.log", "*.csv");

            Log.ZipLog(
                TimeSpan.FromDays(Convert.ToDouble(GLOBAL.config.Get("log_zip_period"))),
                Convert.ToInt32(GLOBAL.config.Get("log_zip_period")),
                GLOBAL.config.Get("history_data_folder") + @"\returns", "*.log", "*.csv");

            Log.ZipLog(
                TimeSpan.FromDays(Convert.ToDouble(GLOBAL.config.Get("log_zip_period"))),
                Convert.ToInt32(GLOBAL.config.Get("log_zip_period")),
                GLOBAL.config.Get("history_data_folder") + @"\datas", "*.log", "*.csv");
            /*
            tasks = new List<Task>();
            
            sw = new Stopwatch();
            sw.Start();
            write_count = 0;
            */
            //System.Threading.Thread.Sleep(100);
            //打开日志文件 日志文件格式 20170101 12-00-00.log
            string log_folder_path = GLOBAL.config.Get("log_folder_path");
            if (!Directory.Exists(log_folder_path))
                Directory.CreateDirectory(log_folder_path);

            string warning_folder_path = GLOBAL.config.Get("warning_folder_path");
            if (!Directory.Exists(warning_folder_path))
                Directory.CreateDirectory(warning_folder_path);

            string history_data_folder = GLOBAL.config.Get("history_data_folder");
            if (!Directory.Exists(history_data_folder))
                Directory.CreateDirectory(history_data_folder);
            /*
            log_folder_path += @"\" + g.GetMondayDate(DateTime.Now).ToString("yyyyMMdd") + "-"
                + g.GetSundayDate(DateTime.Now).ToString("yyyyMMdd");
            if (!Directory.Exists(log_folder_path))
                Directory.CreateDirectory(log_folder_path);
            */
            log_path = log_folder_path + @"\" + DateTime.Now.ToString("yyyyMMdd HH-mm-ss") + @".log";
            log_file = new StreamWriter(log_path, true)
            {
                AutoFlush = true
            };
            Log.Write("日志线程启动");
        }

        /// <summary>
        /// 工作
        /// </summary>
        private void Work()
        {
            if (Log.logs.Count > 150)
            {
                Task.Run(() =>
                {
                    WriteLog();
                });
            }
            else
            {
                WriteLog();
            }

        }

        /// <summary>
        /// 退出
        /// </summary>
        private void Exit()
        {
            try
            {
                //sw.Stop();
                Log.Write("日志线程退出");
                /*
                Log.Write("耗时" + sw.Elapsed);
                Log.Write("共" + write_count + "条日志");
                Log.Write("平均" + (write_count / sw.Elapsed.TotalMilliseconds) + "条日志/毫秒");
                */
                WriteLog();
                log_file.Close();
            }
            catch (EncoderFallbackException e)
            {
                System.Console.WriteLine(e.ToString());
            }
        }

        /// <summary>
        /// 写入文件
        /// </summary>
        private void WriteLog()
        {
            string t_logs = "";
            string[] logs = Log.Read();
            //write_count += logs.Length;
            t_logs = string.Concat(logs);
            //logs.ForEach(l => t_logs += l);//效率低
            if (t_logs != "")
            {
                try
                {
                    log_file.Write(t_logs);
                    Log_stack = t_logs;
                }
                catch (ObjectDisposedException e)
                {
                    System.Console.WriteLine(e.ToString());
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        // This method is called by the Set accessor of each property.
        // The CallerMemberName attribute that is applied to the optional propertyName
        // parameter causes the property name of the caller to be substituted as an argument.
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
